package com.heima.wemedia.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.stream.StreamUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.heima.apis.article.IArticleClient;
import com.heima.common.dtos.ResponseResult;
import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.exception.CustomException;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmSensitive;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.bean.ImageCheckResult;
import com.heima.wemedia.bean.TextCheckResult;
import com.heima.wemedia.service.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Transactional
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {
    @Resource
    private WmNewsService wmNewsService;
    @Resource
    private GreenService greenService;
    @Resource
    private IArticleClient articleClient;
    @Resource
    private WmChannelService wmChannelService;
    @Resource
    private WmUserService wmUserService;

    @Resource
    private WmSensitiveService wmSensitiveService;
    @Resource
    private OCRService ocrService;


    private static final int CONTENT_TYPE_TEXT = 1;
    private static final int CONTENT_TYPE_IMAGE = 2;

    @Override
    public void autoScanWmNews(Integer id) {
        //0.判断检查参数
        if (ObjectUtil.isEmpty(id)) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }


        //1.查询文章
        ResponseResult<WmNews> wmNewsResponseResult = wmNewsService.queryNewsById(id);
        //文章数据
        WmNews wmNews = wmNewsResponseResult.getData();

        //2.进行文本数据检查
        List<String> textList = this.extractTextOrImage(wmNews, CONTENT_TYPE_TEXT);
        Boolean isHandleText = this.handleTextScan(StrUtil.join("", textList), wmNews);
        if (!isHandleText) {
            //说明有不正常数据
            return;
        }

        //3.进行图片数据检查
        List<String> imageList = this.extractTextOrImage(wmNews, CONTENT_TYPE_IMAGE);
        Boolean isHandleImage = this.handleImageScan(imageList, wmNews);
        if (!isHandleImage) {
            return;
        }

        //4.数据没问题，进行保存
        ResponseResult<Long> responseResult = this.saveAppArticle(wmNews);

        if (ObjectUtil.notEqual(responseResult.getCode(), 200)) {
            //说明进行保存不成功,进行事务回滚
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR);
        }

        //保存成功
        wmNews.setArticleId(responseResult.getData());
        this.updateWmNews(wmNews, WmNews.Status.PUBLISHED, "审核通过");

    }

    /**
     * 进行文章数据的提取
     *
     * @param wmNews 文章
     * @param type   提取类型，1表示是文本，2表示是图
     * @return 数据列表
     */
    private List<String> extractTextOrImage(WmNews wmNews, Integer type) {
        //进行文章数据的提取
        JSONArray jsonArray = JSONUtil.parseArray(wmNews.getContent());

        //内容数据的提取
        List<String> list = StreamUtil.of(jsonArray)
                .filter(o -> {
                    JSONObject o1 = (JSONObject) o;
                    String contentType = o1.get("type", String.class);//内容类型
                    if (ObjectUtil.equal(type, CONTENT_TYPE_TEXT)) {
                        return ObjectUtil.equal(contentType, "text");
                    }
                    return ObjectUtil.equal(contentType, "image");
                }).map(o -> {
                    JSONObject o1 = (JSONObject) o;
                    return o1.get("value", String.class);
                }).collect(Collectors.toList());
        //判断是文章还是图片
        if (ObjectUtil.equal(type, CONTENT_TYPE_TEXT)) {
            list.add(wmNews.getTitle());
        }
        //图片
        if (ObjectUtil.equal(type, CONTENT_TYPE_IMAGE)) {
            String[] imageList = wmNews.getImages().split(",");
            list.addAll(Arrays.asList(imageList));
        }
        return list;
    }

    private Boolean handleSensitiveScan(String content, WmNews wmNews) {
        //进行自己敏感词的校验
        List<WmSensitive> wmSensitiveList = wmSensitiveService.list();
        List<String> stringList = wmSensitiveList.stream().map(WmSensitive::getSensitives).collect(Collectors.toList());
        //调用工具类进行查询
        SensitiveWordUtil.initMap(stringList);
        Map<String, Integer> matchWords = SensitiveWordUtil.matchWords(content);
        //判断是否命中
        if (ObjectUtil.isNotEmpty(matchWords)) {
            //不为空，打印敏感词
            List<String> strings = matchWords.entrySet().stream().map(stringIntegerEntry -> StrUtil.format("【{}】出现了【{}】", stringIntegerEntry.getKey(), stringIntegerEntry.getValue()))
                    .collect(Collectors.toList());
            String reason = StrUtil.format("当前文章存在问题,风险等级为高风险，风险词为:{}",
                    StrUtil.join(",", strings));
            this.updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH, reason);
            return false;
        }
        return true;
    }

    /**
     * 进行纯文本数据的校验
     *
     * @param content 文本数据
     * @param wmNews  文章
     * @return 审核结果
     */
    private Boolean handleTextScan(String content, WmNews wmNews) {
        //0.审核参数
        if (ObjectUtil.isEmpty(content)) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        //进行自己敏感词识别
        Boolean handleSensitiveScan = this.handleSensitiveScan(content, wmNews);
        if (!handleSensitiveScan) {
            return false;
        }

        //1.进行导入服务调用
        TextCheckResult textCheckResult = greenService.textCheck(content);
        //进行校验后数据处理

        //检验危险程度
        String riskLevel = textCheckResult.getRiskLevel();

        if (ObjectUtil.equal(riskLevel, "none")) {
            //说明没有风险
            return true;
        }
        if (StrUtil.equalsAny(riskLevel, "medium", "low") || ObjectUtil.isEmpty(textCheckResult)) {
            //说明是中，低风险，以及查询失败
            //转入人工，修改状态
            String reason = StrUtil.format("当前文章存在不确定问题,风险等级为{}，风险词为:{}", riskLevel,
                    StrUtil.join("|", textCheckResult.getRiskWordsList()));
            this.updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH, reason);
            return false;
        }
        //说明是高危
        String reason = StrUtil.format("当前文章存在高危问题,风险等级为{}，风险词为:{}", riskLevel,
                StrUtil.join("|", textCheckResult.getRiskWordsList()));
        this.updateWmNews(wmNews, WmNews.Status.FAIL, reason);
        return false;
    }

    /**
     * 进行图片数据的校验
     *
     * @param images 图片数据
     * @param wmNews 文章
     * @return 审核结果
     */
    private Boolean handleImageScan(List<String> images, WmNews wmNews) {
        //0.审核参数
        if (ObjectUtil.isEmpty(images)) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        ImageCheckResult imageCheckResult = null;
        //1.调用接口进行校验
        for (String image : images) {
            //进行词库的校验
            try {
                BufferedImage bufferedImage = ImageIO.read(new URL(image));
                String ocr = ocrService.doOCR(bufferedImage);
                Boolean handledSensitiveScan = this.handleSensitiveScan(ocr, wmNews);
                if (!handledSensitiveScan) {
                    return false;
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            imageCheckResult = this.greenService.imageCheck(image);
            //进行校验后数据处理

            //检验危险程度
            String riskLevel = imageCheckResult.getRiskLevel();

            if (StrUtil.equalsAny(riskLevel, "medium", "low") || ObjectUtil.isEmpty(imageCheckResult)) {
                //说明是中，低风险，以及查询失败
                //转入人工，修改状态
                String reason = StrUtil.format("当前文章存在不确定问题,风险等级为{}，风险词为:{}", riskLevel,
                        StrUtil.join("|", imageCheckResult.getDescriptionList()));
                this.updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH, reason);
                return false;
            }
            //说明是高危
            String reason = StrUtil.format("当前文章存在高危问题,风险等级为{}，风险词为:{}", riskLevel,
                    StrUtil.join("|", imageCheckResult.getDescriptionList()));
            this.updateWmNews(wmNews, WmNews.Status.FAIL, reason);
            return false;
        }

        //说明检查完都是没问题的
        return true;
    }

    /**
     * 进行保存app端的数据
     *
     * @param wmNews 文章数据
     * @return 操作结果
     */
    private ResponseResult<Long> saveAppArticle(WmNews wmNews) {
        ArticleDto articleDto = BeanUtil.toBean(wmNews, ArticleDto.class);

        //布局设置
        articleDto.setLayout(wmNews.getType());
        //频道设置
        WmChannel channel = wmChannelService.getById(wmNews.getChannelId());
        if (ObjectUtil.isNotEmpty(channel)) {
            //不为空，则设置频道名
            articleDto.setChannelName(channel.getName());
        }
        //作者数据设置
        articleDto.setAuthorId(wmNews.getUserId().longValue());
        WmUser wmUser = wmUserService.getById(wmNews.getUserId());
        if (ObjectUtil.isNotEmpty(wmUser)) {
            articleDto.setAuthorName(wmUser.getName());
        }
        //设置文章id
        articleDto.setId(wmNews.getArticleId());
        articleDto.setCreatedTime(new Date());
        //进行存储数据
        return articleClient.saveArticle(articleDto);
    }


    /**
     * 修改文章内容
     */
    private void updateWmNews(WmNews wmNews, WmNews.Status status, String reason) {
        wmNews.setStatus(status.getCode());
        wmNews.setReason(reason);
        this.wmNewsService.updateById(wmNews);
    }
}
